package com.lizy.edb.connection;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 尝试添加断链重联的策略 和 主备连接策略
 */
public class RabbitUtil {

    private static Map<RabbitResource, Connection> connectionMap = new ConcurrentHashMap<>();
    private static  ThreadLocal<Channel> channelTHreadLocal = new ThreadLocal<>();
    /**
     * 1. 获取连接，如果不存在，就自动创建
     * 2. 一个资源一个连接
     * @param rabbitResource
     * @return
     */
    public static Connection getConnection(RabbitResource rabbitResource) {
        //1. 是否有创建好的连接
        Connection connection = connectionMap.get(rabbitResource);
        if(null == connection) {
            synchronized (connectionMap) {
                if (null == connection) {
                    connection = reTryCreateConnection(rabbitResource);
                    connectionMap.put(rabbitResource,connection);
                }
            }
        }
        return connectionMap.get(rabbitResource);
    }

    /**
     * 关闭连接
     * @param rabbitResource
     */
    public static void closeChannel(RabbitResource rabbitResource) {
        Connection connection = connectionMap.get(rabbitResource);
        if(null != connection) {
            try {
                connection.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 获取通道 (保证一个通道只被一个线程使用)
     * 如果当前线程有创建好的通道就从当前线程拿 否则直接创建
     * @param rabbitResource
     * @return
     */
    public static Channel getChannel(RabbitResource rabbitResource) {
        Channel channel = channelTHreadLocal.get();
        if(null == channel) {
            synchronized (channelTHreadLocal) {
                if(null == channel) {
                    Connection connection = getConnection(rabbitResource);
                    channel = createChannel(connection);
                    channelTHreadLocal.set(channel);
                }
            }
        }
        return channel;
    }

    /**
     * 关闭通道，同时将channel对象从ThreadLocal移出，防止内存溢出
     */
    public static void closeChannel() {
        Channel channel = channelTHreadLocal.get();
        if(channel != null) {
            try {
                channel.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            } catch (TimeoutException e) {
                throw new RuntimeException(e);
            }
            channelTHreadLocal.remove();
        }
    }

    private static Connection createConnection(RabbitConnectionInfo connectionInfo) {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(connectionInfo.getHost());
        connectionFactory.setPort(connectionInfo.getPort());
        connectionFactory.setUsername(connectionInfo.getUsername());
        connectionFactory.setPassword(connectionInfo.getPasswd());
        Connection connection = null;
        try {
            connection = connectionFactory.newConnection();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (TimeoutException e) {
            throw new RuntimeException(e);
        }
        if(null != connection && connection.isOpen()) {
            System.out.println("创建连接成功!");
        }
        return connection;
    }
    /**
     * 带主备和重试机制的创建连接
     * @return
     */
    private static Connection reTryCreateConnection(RabbitResource rabbitResource) {
        if(null != rabbitResource || null != rabbitResource.connectionInfos || rabbitResource.connectionInfos.size()>0) {
            throw new RuntimeException("连接信息不能为空!");
        }
        Connection connection = null;
        // 遍历主备创建连接
        for (int j = 0; j < rabbitResource.getConnectionInfos().size(); j++) {
            RabbitConnectionInfo connectionInfo = rabbitResource.getConnectionInfos().get(j);
            System.out.println("开始创建，连接信息： " + connectionInfo + "的连接");
            // 设置连接失败重试2次
            for (int i = 0; i < 3; i++) {
                ConnectionFactory connectionFactory = new ConnectionFactory();
                connectionFactory.setHost(connectionInfo.getHost());
                connectionFactory.setPort(connectionInfo.getPort());
                connectionFactory.setUsername(connectionInfo.getUsername());
                connectionFactory.setPassword(connectionInfo.getPasswd());
                try {
                    connection = connectionFactory.newConnection();
                } catch (IOException e) {
                    System.out.println("第" + (i+1) + "次创建连接失败，失败原因：" + e + ",连接信息： ....");
                } catch (TimeoutException e) {
                    System.out.println("第" + (i+1) + "次创建连接超时，失败原因：" + e + ",连接信息： ....");
                }
                if(null == connection && i < 2) {
                   sleep(1);
                }
                if(null != connection && connection.isOpen()) {
                    System.out.println("创建连接成功!");
                }
            }
            if(connection == null) {
                System.out.println("连接信息： " + connectionInfo + "创建连接未成功，详情请看日志");
            }
            if(j == (rabbitResource.getConnectionInfos().size()-1)) {
                throw new RuntimeException("创建连接失败，详情请看日志");
            }
        }
        return connection;
    }
    /**
     * 创建Channel
     * @param connection
     * @return
     */
    private static Channel createChannel(Connection connection) {
        Channel channel = null;
        try {
            channel = connection.createChannel();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return channel;
    }

    private static void sleep(int seconds) {
        try {
            TimeUnit.SECONDS.sleep(seconds);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
